<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <script>
    Function.prototype.call = function (thisObj, ...args) {
      console.log('call()')

      // 处理thisObj的特殊值, undefined / null
      if (thisObj===undefined || thisObj===null) {
        // thisObj = window
        return this(...args)
      }

      // this是调用call方法的函数
      // this(...args)  // 不能直接调用

      // 给thisObj添加一个临时方法, 方法属性值当前函数 this
      thisObj.tempFn = this

      // 通过thisObj调用这个临时方法 => 执行的就是当前函数, 函数中的this是thisObj
      const result = thisObj.tempFn(...args)

      // 删除临时方法
      delete thisObj.tempFn

      // 返回方法调用的返回值
      return result
    }

    Function.prototype.apply = function (thisObj, args) {
      console.log('apply()')

      // 处理thisObj的特殊值, undefined / null
      if (thisObj===undefined || thisObj===null) {
        // thisObj = window
        return this(...args)
      }

      // this是调用call方法的函数
      // this(...args)  // 不能直接调用

      // 给thisObj添加一个临时方法, 方法属性值当前函数 this
      thisObj.tempFn = this

      // 通过thisObj调用这个临时方法 => 执行的就是当前函数, 函数中的this是thisObj
      const result = thisObj.tempFn(...args)

      // 删除临时方法
      delete thisObj.tempFn

      // 返回方法调用的返回值
      return result
    }


    function call(fun, thisObj, ...args) {
      console.log('mycall')
      // 处理thisObj的特殊值, undefined / null
      if (thisObj===undefined || thisObj===null) {
        // thisObj = window
        return fun(...args)
      }

      // this是调用call方法的函数
      // this(...args)  // 不能直接调用

      // 给thisObj添加一个临时方法, 方法属性值当前函数 this
      thisObj.tempFn = fun

      // 通过thisObj调用这个临时方法 => 执行的就是当前函数, 函数中的this是thisObj
      const result = thisObj.tempFn(...args)

      // 删除临时方法
      delete thisObj.tempFn

      // 返回方法调用的返回值
      return result
    }

    function apply(fun, thisObj, args) {
      console.log('myapply')
      // 处理thisObj的特殊值, undefined / null
      if (thisObj===undefined || thisObj===null) {
        // thisObj = window
        return fun(...args)
      }

      // this是调用call方法的函数
      // this(...args)  // 不能直接调用

      // 给thisObj添加一个临时方法, 方法属性值当前函数 this
      thisObj.tempFn = fun

      // 通过thisObj调用这个临时方法 => 执行的就是当前函数, 函数中的this是thisObj
      const result = thisObj.tempFn(...args)

      // 删除临时方法
      delete thisObj.tempFn

      // 返回方法调用的返回值
      return result
    }

    // bind(fn, obj, 6)(4, 5)

    // bind返回一个新函数, 新函数内部调用了原函数, 且指定了其this为第一个参数对象
    Function.prototype.bind = function (thisObj, ...args) {
      return (...args2) => {
        return this.call(thisObj, ...args, ...args2)
      }
    }

    function bind(fun, thisObj, ...args) {
      return (...args2) => {
        return call(fun, thisObj, ...args, ...args2)
      }
    }

  </script>

  <script>
    var m = 2
    function fn(a, b) {
      console.log(this, a, b);
      return a + b
    }
    const obj = {m: 1}

    // console.log(fn.call(obj, 2, 3))
    // console.log(fn.call(undefined, 2, 3))
    // console.log(fn.apply(obj, [2, 3]))
    // console.log(fn.apply(undefined, [2, 3]))

    // console.log(call(fn, obj, 2, 3))
    // console.log(call(fn, undefined, 2, 3))
    // console.log(apply(fn, obj, [2, 3]))
    // console.log(apply(fn, undefined, [2, 3]))

    // console.log(fn.bind(obj)(4, 5))   // 4 5 9
    // console.log(fn.bind(obj, 6)(4, 5))
    // console.log(fn.bind(obj, 6, 7)(4, 5))  // bind不仅可以绑定this, 还可以绑定参数
    console.log(bind(fn, obj)(4, 5))   // 4 5 9
    console.log(bind(fn, obj, 6)(4, 5))
    console.log(bind(fn, obj, 6, 7)(4, 5))  // bind不仅可以绑定this, 还可以绑定参数
    

     /* 
    区别方法和属性:
      方法是一种特别的属性, 属性值是函数
    函数的2个角色：
      函数： 本身是可以执行的（内部包含的是可以执行的语句/代码）  fn()
      对象： 函数也是对象   fn.xxx 或者fn['xxx']
    函数的call和apply     fn.call(obj)
      作用：立即调用函数，且指定函数中的this为第一个参数对象
      自定义: 将当前函数添加为对象的临时方法,并通过对象来调用方法, 后面再删除临时方法
    函数的bind     fn.bind(obj)
      作用: 返回一个新函数, 执行函数内部会执行原函数, 且this为bind的第一个参数对象
      自定义: 返回一个新函数, 在新函数内部, 通过原函数的call来执行, 指定this为bind的第一个参数对象
    */
  </script>
</body>
</html>